/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.ui.builders;

import java.text.MessageFormat;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.zoundry.gaedt.core.ZGaeException;
import org.zoundry.gaedt.core.util.ZGaeUtil;


/**
 * Builds a .controllers file into a derived python file.
 */
public class ZGaeBuilder extends IncrementalProjectBuilder
{
    /**
     * C'tor.
     */
    public ZGaeBuilder()
    {
        super();
    }

    /**
     * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
     */
    @Override
    protected IProject[] build(int aKind, Map aArgs, IProgressMonitor aMonitor) throws CoreException
    {
        ZGaeBuildQueue buildQueue;
        
        aMonitor.beginTask("Building...", 2);
        if (aKind == IncrementalProjectBuilder.FULL_BUILD)
        {
            buildQueue = analyzeForFullBuild(aMonitor);
        }
        else
        {
            IResourceDelta delta = getDelta(getProject());
            if (delta == null)
                buildQueue = analyzeForFullBuild(aMonitor);
            else
                buildQueue = analyzeForIncrementalBuild(delta, aMonitor);
        }
        aMonitor.worked(1);
        doBuild(buildQueue, aMonitor);
        aMonitor.worked(1);
        return null;
    }

    /**
     * Analyze the given resource delta and add changed resources to the build
     * queue.
     * 
     * @param aDelta
     * @param aMonitor
     */
    private ZGaeBuildQueue analyzeForIncrementalBuild(IResourceDelta aDelta, IProgressMonitor aMonitor)
    {
        ZGaeIncrementalBuildVisitor visitor = new ZGaeIncrementalBuildVisitor();
        try
        {
            aDelta.accept(visitor);
        }
        catch (CoreException e)
        {
            ZGaeUtil.logError(e);
        }
        return visitor.getBuildQueue();
    }

    /**
     * Analyze the entire project for things to add to the build queue.
     * 
     * @param aMonitor
     */
    private ZGaeBuildQueue analyzeForFullBuild(IProgressMonitor aMonitor)
    {
        ZGaeFullBuildVisitor visitor = new ZGaeFullBuildVisitor();
        try
        {
            getProject().accept(visitor);
        }
        catch (CoreException e)
        {
            ZGaeUtil.logError(e);
        }
        return visitor.getBuildQueue();
    }

    /**
     * Performs the actual build of the changed files.
     * 
     * @param aCspecFiles
     * @param aMonitor
     */
    private void doBuild(ZGaeBuildQueue aBuildQueue, IProgressMonitor aMonitor)
    {
        List<IResource> resources = aBuildQueue.getResources();
        int totalCount = resources.size();
        int counter = 1;
        for (IResource resource : resources)
        {
            String subTaskName = MessageFormat.format("Building resource {0,number,#} of {1,number,#}. [{2}]", 
                    counter++, totalCount, resource.getName());
            aMonitor.subTask(subTaskName);

            List<IZGaeBuildProvider> providers = ZGaeBuildProviderFactory.getInstance().createProviders(resource);
            for (IZGaeBuildProvider provider : providers)
            {
                buildResource(resource, provider);
            }
        }

        List<IResource> removedResources = aBuildQueue.getRemovedResources();
        totalCount = removedResources.size();
        counter = 1;
        for (IResource resource : removedResources)
        {
            String subTaskName = MessageFormat.format(
                    "Building resource {0,number,#} of {1,number,#}. [{2}]", counter++, totalCount);
            aMonitor.subTask(subTaskName);

            List<IZGaeBuildProvider> providers = ZGaeBuildProviderFactory.getInstance().createProviders(resource);
            for (IZGaeBuildProvider provider : providers)
            {
                buildRemovedResource(resource, provider);
            }
        }
    }

    /**
     * Builds a single resource.
     * 
     * @param aResource
     * @param aProvider
     * @throws ZGaeException
     */
    protected void buildResource(IResource aResource, IZGaeBuildProvider aProvider)
    {
        try
        {
            aProvider.build(aResource);
        }
        catch (ZGaeException e)
        {
            ZGaeUtil.logError(e);
        }
    }

    /**
     * Does a remove-build on a single resource.
     * 
     * @param aResource
     * @param aProvider
     * @throws ZGaeException
     */
    protected void buildRemovedResource(IResource aResource, IZGaeBuildProvider aProvider)
    {
        try
        {
            aProvider.removeBuild(aResource);
        }
        catch (ZGaeException e)
        {
            ZGaeUtil.logError(e);
        }
    }
}
